package com.framework.dubbo.producer.invoke.impl;

import com.framework.dubbo.producer.invoke.InvokeCommand;
import com.framework.dubbo.producer.config.Pdf2HtmlProperties;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.exec.*;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

/**
 * 描述：
 * apache-exec调用命令
 * @author xianggj
 * @Date 2021/8/25 10:30
 **/
@Slf4j
@Component("apacheExec")
public class ApacheExecInvokeCommand implements InvokeCommand {


    /**
     * 为了限制同时启动pdf2htmlEX命令行工具的子进程数
     */
    private static Semaphore semaphore;

    @Resource
    private Pdf2HtmlProperties pdf2HtmlProperties;

    @PostConstruct
    public void init() {
        semaphore = new Semaphore(pdf2HtmlProperties.getMaxProcess());
    }


    @Override
    public Map<String, Throwable> invokeCommand(File file, String command, CountDownLatch latch)  throws Throwable {
        // 这里限制启动子进程的数量
        // 因为后面的调用是异步的, 防止瞬间产生大量子进程
        semaphore.acquire();

        // 处理失败的pdf统计
        Map<String, Throwable> failedMap = new ConcurrentHashMap(10);

        // 异步调用pdf2htmlEX命令行工具
        CommandLine commandLine = CommandLine.parse(command);

        // 命令行的超时处理
        ExecuteWatchdog watchdog = new ExecuteWatchdog(1000 * pdf2HtmlProperties.getCommandTimeout().getSeconds());
        // 命令行 执行完成的回调
        ResultHandler resultHandler = new ResultHandler(file, latch, failedMap);
        Executor executor = new DefaultExecutor();
        executor.setExitValue(0);
        executor.setWatchdog(watchdog);

        try {
            executor.execute(commandLine, resultHandler);
        } catch (Throwable e) {
            semaphore.release();
            String fileName = file.getName();
            if (!failedMap.containsKey(fileName)) {
                failedMap.put(fileName, e);
            }
            if (latch != null) {
                latch.countDown();
            }

            log.error("invokeCommand failed, command: {}, error:{}", command, e);
        }
        return failedMap;

    }

    /**
     * 获取并发信息
     * @return
     */
    @Override
    public Map<String, Object> getMetric(){
        Map<String, Object> semaphoreMap = new LinkedHashMap<>();
        semaphoreMap.put("availablePermits", semaphore.availablePermits());
        semaphoreMap.put("queueLength", semaphore.getQueueLength());

        Map<String, Object> metricMap = new LinkedHashMap<>();
        metricMap.put("semaphore", semaphoreMap);
        return semaphoreMap;
    }

    /**
     * 处理结果
     */
    public static class ResultHandler implements ExecuteResultHandler {


        private final File file;
        private final CountDownLatch latch;
        private final Map<String, Throwable> failedMap;

        @Getter
        private int exitValue = -8686;

        public ResultHandler(File file, CountDownLatch latch, Map<String, Throwable> failedMap) {
            this.file = file;
            this.latch = latch;
            this.failedMap = failedMap;
        }

        @Override
        public void onProcessComplete(int exitValue) {
            semaphore.release();
            if (latch != null) {
                this.latch.countDown();
            }

            this.exitValue = exitValue;
        }

        @Override
        public void onProcessFailed(ExecuteException e) {
            semaphore.release();
            this.failedMap.put(this.file.getName(), e);
            if (latch != null) {
                this.latch.countDown();
            }

            log.error("pdf2html failed, file: {}, error:{}", this.file.getAbsolutePath(), e);
        }
    }

}
